#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdarg.h>
#include <time.h>

int DirNum=0;
struct monitorNodes {
	int wd;
	char *path;
	struct monitorNodes *next;
};
struct monitorNodes *lastNode;

enum {
    NTF_CREATE,
    NTF_DELETE,
    NTF_MODIFY,
    NTF_MOVEFR,
    NTF_MOVETO,
    NTF_MOVESLF,
    NTF_DELSLF
};

static void handle_events(int fd, struct monitorNodes *mNode)
{
	/* Some systems cannot read integer variables if they are not
    properly aligned. On other systems, incorrect alignment may
    decrease performance. Hence, the buffer used for reading from
    the inotify file descriptor should have the same alignment as
    struct inotify_event. */

	char buf[4096]
         __attribute__ ((aligned(__alignof__(struct inotify_event))));
    const struct inotify_event *event;
    int i;
    ssize_t len;
    char *ptr=NULL;
	struct monitorNodes *tmp;
    char *tmp_action=NULL;
    int action=-1;
    char *dpath, *tmppath;
    FILE *fpr;
    char cmd[256],node[256];

    /* Loop while events can be read from inotify file descriptor. */
    
	for (;;) {

        /* Read some events. */

        len = read(fd, buf, sizeof buf);
        if (len == -1 && errno != EAGAIN) {
            perror("read");
            exit(EXIT_FAILURE);
        }

        /* If the nonblocking read() found no events to read, then
           it returns -1 with errno set to EAGAIN. In that case,
           we exit the loop. */

        if (len <= 0)
            break;

        /* Loop over all events in the buffer */

        for (ptr = buf; ptr < buf + len;
                ptr += sizeof(struct inotify_event) + event->len) {

            event = (const struct inotify_event *) ptr;

            /* Print event type */
            /* IN__CREATE | IN_DELETE | IN_MODIFY | IN_MOVE 
			| IN_DELETE_SELF | IN_MOVE_SELF);*/
                
			if (event->mask & IN_CREATE)
            {
				LOG("IN_CREATE: ");
                action = NTF_CREATE;
            }
			if (event->mask & IN_DELETE)
            {
				LOG("IN_DELETE: ");
                action = NTF_DELETE;
            }
			if (event->mask & IN_MODIFY)
            {
				LOG("IN_MODIFY: ");
                action = NTF_MODIFY;
            }
			if (event->mask & IN_MOVED_FROM)
            {
				LOG("IN_MOVED_FROM: ");
                action = NTF_MOVEFR;
            }
			if (event->mask & IN_MOVED_TO)
            {
				LOG("IN_MOVED_TO: ");
                action = NTF_MOVETO;
            }
			if (event->mask & IN_MOVE_SELF)
            {
				LOG("IN_MOVE_SELF: ");
                action = NTF_MOVESLF;
            }
			if (event->mask & IN_DELETE_SELF)
            {
			    LOG("IN_DELETE_SELF: ");
                action = NTF_DELSLF;
            }

            /* Print the name of the watched directory */
			for (tmp = mNode->next; tmp; tmp = tmp->next) {
				if (tmp->wd == event->wd){
                    if(tmp->path==NULL)
                    LOG("get path is NULL");
                    else{
                    LOG("%s/", tmp->path);
                        tmppath=tmp->path;       
                    LOG("tmp %s", tmppath);
                    if(strcmp(tmppath,"")==0)
                    {
                        LOG("tmppath is null");
                    }
                    }    
                }
			}
			
			/* Print the name of the file */

            if (event->len)
            {
                LOG("%s", event->name);
                /*
                sprintf(cmd,"stat %s > /tmp/tmp_ino",event->name);
                system(cmd);
                system("cat /tmp/tmp_ino |grep Inode |awk '{print $4}' > /tmp/f_ino");
                fpr=fopen("/tmp/f_inode","r");
                fgets(node,256,fpr);
                printf("inode is %s\n",node);
                */
              //  sprintf(cmd,"find ./ -inum %s",node);
            //    system(cmd);
            }

            /* Print type of filesystem object */

            if (event->mask & IN_ISDIR)
            {
                if(action == NTF_CREATE )
                {
                    LOG("for test");
                    int len = strlen(tmppath) + strlen(event->name) + 2;
                    dpath = (char *)malloc(len * sizeof(char));
                    LOG("add to wath");
                    LOG("%s/", tmppath);
                    LOG("%s", event->name);
                    snprintf(dpath, len, "%s/%s", tmppath,event->name);
                    LOG("dpath is %s", dpath);
                    addDirToWatch(fd, dpath);
                    free(dpath);
                }

            }
            else
                LOG(" [file]");
            
			//print cookie		
			if (event->cookie != 0)
				LOG("cookie %d", event->cookie);
        }
	}
}

int addDirToWatch(int fd, char* dir)
{
    /* Allocate memory for watch descriptors */
	int wd=0;
   
	/* Mark directories for events
       - file was created/modified/deleted */

	//LOG("add %s", dir);
	
	wd = inotify_add_watch(fd, dir,
            IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVE 
			| IN_DELETE_SELF | IN_MOVE_SELF);
	
	if (wd == -1) {
		LOG("Cannot watch '%s'", dir);
        perror("inotify_add_watch");
        exit(EXIT_FAILURE);
    }

	lastNode->next = (struct monitorNodes *)malloc(sizeof(struct monitorNodes));
	lastNode=lastNode->next;
    lastNode->wd = wd;
	lastNode->path = dir;
	lastNode->next = NULL;
	
	DIR *dp;
	struct dirent *dirp;
	if((dp=opendir(dir))==NULL){
		LOG("Open dir %s failed, errmsg:%s", dir, strerror(errno));
        exit(EXIT_FAILURE);
	}
	
	while((dirp=readdir(dp))!=NULL)
	{
		char *pathname;
		struct stat statbuf;

		if (dirp->d_name[0] == '.') continue;
	   
        int len = strlen(dir) + strlen(dirp->d_name) + 2; 
        pathname = (char *)malloc(len * sizeof(char));    
		snprintf(pathname, len, "%s/%s", dir, dirp->d_name);
		
		if (lstat(pathname, &statbuf)){ 
			LOG("lstat %s failed", pathname); 
			exit(EXIT_FAILURE);
		}

		if(S_ISDIR(statbuf.st_mode)){
			addDirToWatch(fd, pathname); 
			//LOG("traverse %s", pathname);
		}
	}
}

int main(int argc, char* argv[])
{ 
	int fd, poll_num;
	char usbPath[128], name[128], dir[256];
    struct pollfd fds[2];
	time_t now;
    struct monitorNodes *mNode;

/*	if(daemon(1, 1) == -1){
	   fprintf(stderr, "daemon failed %d: %s", errno, strerror(errno));
	   return 0;
	}*/
    /* Create the file descriptor for accessing the inotify API */
	LOG("timestamp: %ld", time(&now));

	fd = inotify_init1(IN_NONBLOCK);
    if (fd == -1) {
		perror("inotify_init1");
		exit(EXIT_FAILURE);
    }
	
	strcpy(name, config_get("sync_local_folder"));
	strcpy(usbPath, config_get("usb_path"));
	sprintf(dir, "%s/%s", usbPath, name);
	LOG("sync dir %s", dir);
	
	mNode = (struct monitorNodes *)malloc(sizeof(struct monitorNodes));
	mNode->path = (char *)malloc(sizeof(256));
	mNode->next=NULL;
    lastNode = mNode;
	
	addDirToWatch(fd, dir);	
/*   
	struct monitorNodes *tmp;
	for (tmp = mNode; tmp; tmp = tmp->next) {
		fprintf(stderr, "wd %d: %s/\n", tmp->wd, tmp->path);
	}

	exit(0);*/
	/* Inotify input */

	fds[0].fd = fd;
    fds[0].events = POLLIN;

	LOG("timestamp: %ld", time(&now));
	/* Wait for events*/

    LOG("Listening for events.");
   
    while (1) {
        LOG("get event-----");
		poll_num = poll(fds, 1, -1);
        if (poll_num == -1) {
            if (errno == EINTR)
                continue;
			perror("poll");
	        exit(EXIT_FAILURE);
        }

        if (poll_num > 0) {

			if (fds[0].revents & POLLIN) {
		
				/* Inotify events are available */
				handle_events(fd, mNode);
			}
        }
    }

    /* Close inotify file descriptor */
    free(mNode);
    close(fd);

    exit(EXIT_SUCCESS);
}

void LOG(char *format, ...) 
{
	va_list ap;
	int ret;
	char buf[1024];

	va_start(ap, format);
	ret = vsprintf(buf, format, ap);
	va_end(ap);

	if(ret >= 0) {
		printf(buf);
		printf("\n");		
	}
}
